/**************************************************************************(C)*
 *
 *  This file is part of the GreenHills project
 *  Copyright (C) 2009  Tieto Corporation
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  Not for commmercial use. Only with Tieto permission.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************(C)*/

#include "../msgsocketconnection.h"
#include "io/message.h"
#include "io/processexception.h"

using namespace greenhills::net;
using namespace greenhills::io;

CMsgSocketConnection::CMsgSocketConnection(SOCKET_TYPE pSocket)
        : io::CConnection(), mSocket(pSocket), mReceivingMessage(0, 0),
        mReceivingMessageLength(0), mBytesReceived(0)
{
}

CMsgSocketConnection::~CMsgSocketConnection()
{
    close();
}

void CMsgSocketConnection::process()
{
    if (mSocket == INVALID_SOCKET) {
        throw io::CProcessException("Socket isn't opened.");
    }

    // sending
    while (!mMessagesToSendList.empty()) {
        CMessage *message = mMessagesToSendList[0];

        int bytesSent = send(mSocket,
                             message->getRemainingData(),
                             message->getRemainingLength(),
                             0);

        if (bytesSent < 0) {
            throw io::CProcessException("QIODevice::write returned -1");
        }

        message->increasePosition(bytesSent);

        if (message->getRemainingLength() > 0) {
            break;
        }

        mMessagesToSendList.erase(mMessagesToSendList.begin());
        delete message;
    }

    // receiving
    while (true) {
        int bytesReceived;

        if (mBytesReceived < sizeof(mReceivingMessageLength)) {
            bytesReceived = recv(mSocket,
                    ((char*)&mReceivingMessageLength) + mBytesReceived,
                    sizeof(mReceivingMessageLength) - mBytesReceived,
                    0);

            if (bytesReceived < 1) {
                break;
            }

            mBytesReceived += bytesReceived;

            if (mBytesReceived < sizeof(mReceivingMessageLength)) {
                break;
            }

            mReceivingMessage.setData(new char[mReceivingMessageLength],
                                      mReceivingMessageLength);
        }

        bytesReceived = recv(mSocket,
                             (char*)mReceivingMessage.getRemainingData(),
                             mReceivingMessage.getRemainingLength(),
                             0);

        if (bytesReceived < 1) {
            break;
        }

        mReceivingMessage.increasePosition(bytesReceived);

        if (mReceivingMessage.getRemainingLength() == 0) {
            emitDataReceivedEvent(mReceivingMessage.getData(),
                                  mReceivingMessage.getLength());

            mReceivingMessage.clear();
            mReceivingMessageLength = 0;
            mBytesReceived = 0;
        }
    }
}

void CMsgSocketConnection::close()
{
    if (mSocket != INVALID_SOCKET) {
#if defined Q_OS_LINUX
        shutdown(mSocket, SHUT_RDWR);
#elif defined Q_OS_WIN32
        closesocket(mSocket);
#elif defined Q_OS_WINCE
        closesocket(mSocket);
#endif
        mSocket = INVALID_SOCKET;
    }
}

bool CMsgSocketConnection::isOpened()
{
    return mSocket != INVALID_SOCKET;
}

bool CMsgSocketConnection::operator == (const io::CConnection &pConnection)
{
    const CMsgSocketConnection *connection =
            dynamic_cast<const CMsgSocketConnection*>(&pConnection);

    if (connection == 0)
        return false;

    return mSocket == connection->mSocket;
}
